Skip to main content
Version: Current

七、系统使用说明

7.1 使用 GPIO

EA65xx 芯片支持三组 GPIO,分别命名为 SAP GPIO、CPU GPIO 和 RTC GPIO。在每个 GPIO 组内对信号从 0 开始编址,称为物理编号,此外,软件还对全部 GPIO 信号做了统一编址,称为逻辑编号,其编号规则是:

GPIO 逻辑编号 = GPIO 所属组 BASE 号 + 组内物理编号

其中,三个组的 BASE 号分别为 0、200、400。BASE 号的来源是根据 GPIO 组的命名和系统设计而定,以确保逻辑编号的唯一性和可识别性。于是,根据上述规则,GPIO 与 Linux 设备节点的对应关系如下表所示:

GPIO 组别Linux 设备节点物理编号范围逻辑编号范围
SAP GPIO/sys/class/gpio/gpiochip0/0 到 330 到 33
CPU GPIO/sys/class/gpio/gpiochip200/0 到 142200 到 342
RTC GPIO/sys/class/gpio/gpiochip400/0 到 7400 到 407

以 GPIO29 为例,它属于 SAP 组的原因是其逻辑编号为 29,根据逻辑编号计算规则,29 落在 SAP 组的逻辑编号范围内(0 到 33)。要正确判断 GPIO 的归属,可以根据其逻辑编号与上述表格中的逻辑编号范围进行匹配,可以通过以下命令进行操作:

  1. 导出 GPIO:将 GPIO 节点导出到 /sys/class/gpio/export
echo 29 > /sys/class/gpio/export
  1. 设置 GPIO 方向:设置为输入或输出。
echo out > /sys/class/gpio/gpio29/direction
  1. 读写 GPIO 值
echo 1 > /sys/class/gpio/gpio29/value  # 设置为高电平
cat /sys/class/gpio/gpio29/value # 读取当前值

7.2 使用 UART

EM20-DK 开发板提供了 12 组 UART,其中,EM20-DK 对外支持 UART0 已用作 bootloader 和 Linux 的 console 端口; UART5 用户可看情况配置使用。其他 UART 的可用性取决于具体的硬件设计和配置。用户可以通过以下命令测试 UART 通信:

root@taco-dk:~# echo "Hello World" > /dev/ttyPS0
Hello World

ttyPS0 为相应的 UART 设备文件,有 ttyPS0ttyPS1ttyPS2 等。如果 UART 不可用,可能是因为硬件设计或配置问题。

以下是一个示例代码,展示如何配置和使用 UART:

int set_interface_attribs(int fd, int speed, int hwfc) {
struct termios tty;
if (tcgetattr(fd, &tty) != 0) {
perror("tcgetattr");
return -1;
}

cfsetospeed(&tty, speed);
cfsetispeed(&tty, speed);

tty.c_cflag = (tty.c_cflag & ~CSIZE) | CS8;
tty.c_iflag &= ~IGNBRK;
tty.c_lflag = 0;
tty.c_oflag = 0;
tty.c_cc[VMIN] = 0;
tty.c_cc[VTIME] = 5;

tty.c_iflag &= ~(IXON | IXOFF | IXANY);
tty.c_cflag |= (CLOCAL | CREAD);
tty.c_cflag &= ~(PARENB | PARODD);
tty.c_cflag &= ~CSTOPB;
if (hwfc) {
tty.c_cflag |= CRTSCTS; // Enable hardware flow control
} else {
tty.c_cflag &= ~CRTSCTS;
}

if (tcsetattr(fd, TCSANOW, &tty) != 0) {
perror("tcsetattr");
return -1;
}
return 0;
}

void set_blocking(int fd, int should_block) {
struct termios tty;
memset(&tty, 0, sizeof tty);
if (tcgetattr(fd, &tty) != 0) {
perror("tcgetattr");
return;
}

tty.c_cc[VMIN] = should_block ? 1 : 0;
tty.c_cc[VTIME] = 5;

if (tcsetattr(fd, TCSANOW, &tty) != 0)
perror("tcsetattr");
}

int rs485_send_test() {
int fd = open(rs485_port.c_str(), O_RDWR | O_NOCTTY | O_SYNC);
if (fd < 0) {
perror("open RS485 port failed");
return -1;
}

if (set_interface_attribs(fd, rs485_baudrate, 0) != 0) {
close(fd);
return -1;
}
set_blocking(fd, 0);

std::string full_data = rs485_test_data + "\r\n";
int ret = write(fd, full_data.c_str(), full_data.length());
if (ret < 0) {
perror("RS485 write failed");
close(fd);
return -1;
}
DEBUG_INFO("Sent %d bytes: %s", ret, full_data.c_str());

close(fd);
return 0;
}

功能说明:

  • set_interface_attrs 函数用于配置 UART 接口属性,如波特率、数据位、停止位、校验位等。
  • set_blocking 函数用于设置 UART 接口的阻塞模式。
  • rs485_send_test 函数用于打开指定的 UART 端口,配置其属性,并通过该端口发送数据。

通过上述代码,用户可以配置 UART 接口并通过该接口发送数据。具体配置和功能取决于实际的硬件设计和需求。

7.3 使用 I2C

提供了 8 路 I2C,可以使用标准的 I2C tools 和 API 操作。

  1. i2cdetect 命令输出已安装 I2C 总线的列表:
root@taco-dk:~# i2cdetect -l
i2c-0 i2c Cadence I2C at d2400000 I2C adapter
i2c-1 i2c Cadence I2C at d2401000 I2C adapter
i2c-2 i2c Cadence I2C at c2401000 I2C adapter
  1. i2cset 配置 I2C 外设
i2cset -f -y $I2C_BUS $I2C_ADDR $reg $value
  1. i2cget 获取 I2C 外设的值
i2cget -f -y $I2C_BUS $I2C_ADDR $reg w

参数解释:

  • $I2C_BUS:I2C 总线号。
  • $I2C_ADDR:I2C 设备的地址。
  • $reg:寄存器地址。
  • $value:要写入的值。

以下为一个示例,展示如何使用 i2cseti2cget 命令:

# 设置 I2C 设备寄存器值
i2cset -f -y 1 0x50 0x00 0x01

# 获取 I2C 设备寄存器值
i2cget -f -y 1 0x50 0x00

7.4 使用 PWM

PWM(脉冲宽度调制)是一种常用的信号生成技术,用于控制模拟信号的占空比。操作 PWM 的示例如下:

  1. 启用 PWM
echo 0 > /sys/class/pwm/pwmchip0/pwm0/export

每个 PWM 通道下有以下文件:

  • period:设置 PWM 周期,单位为纳秒(ns)。
  • duty_cycle:设置 PWM 占空比。
  • enable:启用或禁用 PWM 通道。
  1. 配置 PWM
echo 1000 > /sys/class/pwm/pwmchip0/pwm0/period           # 设置 PWM 周期为 1ms
echo 500 > /sys/class/pwm/pwmchip0/pwm0/duty_cycle # 设置 PWM 占空比

这表明设置 PWM0 的周期为 1ms,占空比为 50%。

  1. 使能 PWM
echo 1 > /sys/class/pwm/pwmchip0/pwm0/enable

在我们的参考底板上,这个 PWM 输出被用作风扇调速,Linux 的 thermal 框架会自动根据芯片温度调整风扇转速。所以用户会在第一步 export 时看到 Device or resource busy 错误,需要修改设备树把对应的 pwmfan 节点 disable 掉后才能自由使用:

fan0: fan {
compatible = "pwm-fan";
pwms = <&sappwm0 0 10000 0>;
cooling-levels = <255 192 128 64 1>;
#cooling-cells = <2>;
};

7.5 查询硬件温度

使用命令 cat /sys/class/thermal/thermal_zone0/temp 获取 SoC 芯片温度。该命令返回的温度数值单位为毫摄氏度(mC)。

root@taco-dk:~# cat /sys/class/thermal/thermal_zone0/temp
26816

上述命令返回的数值 26816 表示芯片温度为 26.816°C。

使用命令 cat /sys/class/thermal/thermal_zone1/temp 获取核心板温度。该命令返回的温度数值单位同样为毫摄氏度(mC)。

root@taco-dk:~# cat /sys/class/thermal/thermal_zone1/temp
30250

7.6 查询内存信息

EM20-DK 板载了 14GB DDR,可以分为三类:

  1. OS 管理的部分,即可以用 mallockmalloc 等常规 API 分配出来使用。
root@taco-mes20:~# free -h
total used free shared buff/cache available
Mem: 9.6Gi 268Mi 9.3Gi 1.1Mi 121Mi 9.4Gi
Swap: 0B 0B 0B
  1. taco-sys 管理的部分,预留给 NPU、VENC、VDEC 使用,需要使用 libtacosys.so 库接口使用,可通过启动介质中 bootfs 分区里 config.txt 配置(默认值是8G),可以按需修改:
##########           Memory Configuration           ##########
# Uncomment this to set tacosys memory address to 0x140000000,
# and size to 0x80000000 bytes (2GiB)
# tacosys_mem_addr=0x140000000
# tacosys_mem_size=0x80000000

下面是一些使用 libtacosys 接口的实例代码,调用 npu_usage_tcpu_usage_t 结构体来获取使用情况:

    taco_npu_usage_t npu_usage = {0};
if (taco_sys_get_npu_usage(&npu_usage) == TACO_SUCCESS) {
printf("NPU Usage: %d%%\n", npu_usage.npu_usage);
} else {
printf("Failed to get NPU usage\n");
}

taco_cpu_usage_t cpu_usage = {0};
if (taco_sys_get_cpu_usage(&cpu_usage) == TACO_SUCCESS) {
printf("Total CPU Usage: %d%%\n", cpu_usage.total_cpu_usage);
for (int i = 0; i < 8; i++) {
printf("CPU%d Usage: %d%%\n", i, cpu_usage.cpu_usage[i]);
}
} else {
printf("Failed to get CPU usage\n");
}
  1. NPU 管理的部分,专门预留给 NPU(下面所示是注释掉的默认值),可以按需修改:
# Uncomment this to set npu memory address to 0x1c0000000,
# and size to 0x280000000 bytes (10GiB)
# npu_mem_addr=0x1c0000000
# npu_mem_size=0x280000000

这些命令将显示 tacosys 和 NPU 内存的实际分配情况。要验证配置是否生效,可以通过重启设备并再次检查这些文件的内容。